Categories
JavaScript Best Practices

JavaScript Clean Code — Law of Demeter

Spread the love

The Law of Demeter states that we should hide the implementation details of our code as much as we can. This ensures that we have loose coupling between our code.

In this article, we’ll look at how to apply the Law of Demeter to our code.

Why do we Need Loose Coupling?

Loose coupling means that we don’t have to worry about changing a lot of code when one piece of code changes.

Each unit only has limited knowledge about other units so that only a few parts of the other units are referenced.

They only talk to their immediate friends, so that they don’t talk to other parts that aren’t related.

So this means that if we have 2 classes A and B , then A only references the methods of B than has to be referenced. Other members are kept private along with other implementation details.

How do we apply the Law of Demeter?

We can apply the Law of Demeter to our JavaScript code by writing it in a way that references a few classes and members of them as possible.

An example of classes that references each other too much is the following:

class PostalCode {
  constructor(postalCode) {
    this.postalCode = postalCode;
  }

  setPostalCode(postalCode) {
    this.postalCode = postalCode;
  }
}

class Address {
  constructor(streetName) {
    this.streetName = streetName;
  }

  getPostalCode() {
    return this.postalCode;
  }

  setPostalCode(postalCode) {
    this.postalCode = new PostalCode(postalCode);
  }
}

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  setAddress(address) {
    this.address = new Address(address);
  }

  getAddress() {
    return this.address;
  }
}

We have the Address class that references PostalCode, and Person class that references Address and Occupation .

If any of Address and PostalCode change, then we have to change the Person and Address class.

Also, we have to set the postal code of a Person by writing:

person.getAddress().getPostalCode().setPostalCode('12345');

That’s a lot of chaining involving returning instances of different classes. If any of these methods change, then the whole chain has to be rewritten.

What we should do instead is to combine all the references into one method as follows:

class PostalCode {
  constructor(postalCode) {
    this.postalCode = postalCode;
  }

  setPostalCode(postalCode) {
    this.postalCode = postalCode;
  }
}

class Address {
  constructor(streetName) {
    this.streetName = streetName;
  }

  getPostalCode() {
    return this.postalCode;
  }

  setPostalCode(postalCode) {
    this.postalCode = new PostalCode(postalCode);
  }
}

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  setAddress(address) {
    this.address = new Address(address);
  }

  getAddress() {
    return this.address;
  }

  getPostalCode() {
    return this.postalCode;
  }

  setPostalCode(postalCode) {
    this.postalCode = new PostalCode(postalCode);
  }
}

Then we only have to update the Person class if the PostalCode class changes instead of updating the whole chain of calls just to update the getting and setting the postal code when the PostalCode class changes.

The point is that we should have to know the whole system to get something done.

PostalCode doesn’t have to be connected to Address since they can be changed individually.

If we couple them together, then we have to know about Address before changing PostalCode .

The example above shows coupling that can be avoided and it should be.

Facade Pattern

We can also use the facade pattern to hide the complexity of a system so that we don’t have to know about them.

For example, we can hide multiple classes behind a facade class and then use the facade class to indirectly interact with multiple classes that hide behind the facade class as follows:

class ClassA {

}

class ClassB {

}

class ClassC {

}

class Facade {
  constructor() {
    this.a = new ClassA();
    this.b = new ClassB();
    this.c = new ClassC();
  }
}

class Foo {
  constructor() {
    this.facade = new Facade();
  }

}

In the example above, the Foo class knows nothing about what’s behind the Facade class. The Facade class holds instances of ClassA , ClassB , and ClassC .

It provides a simple interface to a complex system which consists of ClassA , ClassB , and ClassC .

When any of the classes behind Facace changes, we just have to change the Facade class.

It serves as an entry point to all we want to do with those classes. Instead of referencing them all individually and creating a mess of references, we have one unified interface for all of them.

This satisfies the Law of Demeter because we only access the Facade class to do anything with ClassA , ClassB , and ClassC . We don’t have to know about the underlying implementation of them.

It makes the software easy to use, understand and test since we only have to use and test Facade to interact with all the classes underneath.

It removed the need to reference multiple parts of a complex system since the Facade class provides all we need.

If the code underneath the facade class is poorly designed, we can also wrap around it with a well-designed API that helps people using the facade class to work with it in an easy way.

Most importantly, tight coupling is eliminated since nothing but the facade class references the complex code underneath it.

Conclusion

The Law of Demeter is all about hiding implementation as much as possible to outside code so that they won’t have to reference different parts of the code to get something done.

We should only create classes that talk to closely related classes, rather than talking to everything.

Talking to everything creates a mess of references that’s hard to figure out when we need to change the code.

One good way to implement the laws in the Law of Demeter is to use the Facade pattern. The pattern states that we have a facade class that serves as the entry point for other classes which are part of a complex system.

It’s used to provide an easy to use API which hides the implementation underneath. So if we do need to change the code underneath, we only have to update the facade class rather than everything that references the implementation code underneath.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *